package com.yeyks.appcontroller;

import com.yeyks.base.Arguments;
import com.yeyks.dal.domain.UserInfo;
import com.yeyks.dto.BankBindDTO;
import com.yeyks.dto.UserInfoDTO;
import com.yeyks.exception.ServiceException;
import com.yeyks.exception.SmsSendException;
import com.yeyks.model.Ajax.AjaxResponseBody;
import com.yeyks.model.em.AccountType;
import com.yeyks.service.*;
import com.yeyks.util.InvateCode;
import com.yeyks.util.ObjectConvertUtil;
import com.yeyks.util.TextValidator;
import com.yeyks.util.Utility;
import com.yeyks.service.OssService;
import com.yeyks.service.SmsService;
import io.swagger.annotations.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.FilenameUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import springfox.documentation.annotations.ApiIgnore;

import java.text.SimpleDateFormat;
import java.util.*;

/**
 * @Author fuchangshuai
 * @date 2019/3/26 3:12 PM
 */
@Api(tags = "客户端用户接口", description = "用户")
@Slf4j
@RestController("appUserInfoController")
@RequestMapping(value = "/app/user")
public class UserInfoController {

    @Autowired
    private UserInfoService userInfoService;
    @Autowired
    private SmsService smsService;
    @Autowired
    private UserCouponService userCouponService;
    @Autowired
    private OssService ossService;
    @Autowired
    private TokenService tokenService;

    /**
     * 获得短信验证码用于注册
     *
     * @param phone 需要创建账号的电话号码
     * @return
     */
    @ApiOperation(value = "获得短信验证用于注册")
    @ApiImplicitParam(paramType = "query", name = "phone", value = "手机号", required = true, dataType = "String")
    @RequestMapping(value = "/getVerifyCode", method = RequestMethod.POST)
    public ResponseEntity<?> getVerifyCode(@RequestParam(name = "phone") String phone) {
        checkParamPhone(phone);
        return sendMessage(phone);
    }

    /**
     * 验证发送的注册短信是否正确
     *
     * @param phone
     * @param code
     * @return
     */
    @ApiOperation(value = "验证发送的注册短信是否正确")
    @ApiImplicitParams({
            @ApiImplicitParam(paramType = "query", name = "phone", value = "手机号", required = true, dataType = "String"),
            @ApiImplicitParam(paramType = "query", name = "code", value = "验证码", dataType = "String")
    })
    @RequestMapping(value = "/verifyCode", method = RequestMethod.POST)
    public ResponseEntity<?> verifyCode(@RequestParam(name = "phone") String phone,
                                        @RequestParam(name = "code") String code) {
        boolean flag = smsService.verifySmsForSignup(phone, code);
        if (flag) {
            return ResponseEntity.ok(AjaxResponseBody.success(true));
        }
        return ResponseEntity.ok(AjaxResponseBody.failed(-1, "验证码有误"));
    }

    /**
     * 忘记密码发送验证码
     *
     * @param phone
     * @return
     */
    @ApiOperation(value = "忘记密码发送验证码")
    @ApiImplicitParam(paramType = "query", name = "phone", value = "手机号", required = true, dataType = "String")
    @PostMapping(value = "/forgetPwdGetCode")
    public ResponseEntity<?> getPasswordVerifyCode(@RequestParam(name = "phone") String phone) {
        checkParamPhone(phone);
        UserInfo userInfo = userInfoService.selectUseInfoPhone(phone);
        if (userInfo == null) {
            return ResponseEntity.ok(AjaxResponseBody.failed(-19950910, "该用户不存在"));
        }
        return sendMessage(phone);
    }

    /**
     * 用户登录
     *
     * @param code
     * @param phone
     * @param password
     * @return
     */
    @ApiOperation(value = "用户登陆", notes = "使用手机号验证码或者手机号密码登陆")
    @ApiImplicitParams({
            @ApiImplicitParam(paramType = "query", name = "code", value = "验证码", dataType = "String"),
            @ApiImplicitParam(paramType = "query", name = "phone", value = "手机号", required = true, dataType = "String"),
            @ApiImplicitParam(paramType = "query", name = "password", value = "密码", dataType = "String"),
            @ApiImplicitParam(paramType = "query", name = "sourceType", value = "来源", defaultValue = "ios", dataType = "String")
    })
    @PostMapping(value = "/login")
    public ResponseEntity<?> loginV2(@RequestParam(name = "code", required = false) String code,
                                     @RequestParam(name = "phone") String phone,
                                     @RequestParam(name = "password", required = false) String password,
                                     @RequestParam(name = "sourceType", defaultValue = "ios") String sourceType) {
        //非验证码登陆
        if (code == null || code.isEmpty()) {
            checkParamPhonePwd(phone, password);
            //用户的话使用手机号密码登陆
            UserInfo userInfo = userInfoService.selectUseInfoPhone(phone);
            if (userInfo == null) {
                throw new ServiceException("该用户不存在");
            }
            if (!password.equals(userInfo.getPassword())) {
                throw new ServiceException("密码输入有误");
            }
            return packLoginResult(userInfo);
        }
        checkParamPhone(phone);
        if (smsService.verifySmsForSignup(phone, code)) {
            // 查找用户的所有信息
            UserInfo exist = userInfoService.selectUseInfoPhone(phone);
            if (exist != null) {
                return packLoginResult(exist);
            } else {
                UserInfo userInfo = getUserInfo(sourceType, phone);
                userInfoService.insertReturnId(userInfo);
                if (userInfo.getId() != null) {
                    userInfo.setInviteCode(InvateCode.toSerialCode(userInfo.getId()));
                    int res = userInfoService.updateUserInfo(userInfo);
                    if (res != 1) {
                        log.info("更新邀请码异常");
                    }
                    //给新用户发两张10元优惠券
                    userCouponService.createNewCoupon(userInfo.getId());
                }
                return packLoginResult(userInfo);
            }
        } else {
            return ResponseEntity.ok(AjaxResponseBody.failed("验证码无效"));
        }
    }

    /**
     * 分享后用户通过手机号注册
     *
     * @param userId
     * @param phone
     * @return
     */
    @ApiOperation(value = "分享后用户通过手机号注册", notes = "分享后用户通过手机号注册")
    @ApiImplicitParams({
            @ApiImplicitParam(paramType = "query", name = "userId", value = "分享人用户id", required = true, dataType = "int"),
            @ApiImplicitParam(paramType = "query", name = "phone", value = "手机号", required = true, dataType = "String"),
            @ApiImplicitParam(paramType = "query", name = "sourceType", value = "来源", defaultValue = "ios", dataType = "String")
    })
    @PostMapping(value = "/phone/login")
    public ResponseEntity<?> phoneLogin(@RequestParam(name = "userId") Integer userId,
                                        @RequestParam(name = "phone") String phone,
                                        @RequestParam(name = "sourceType", defaultValue = "ios") String sourceType) {
        checkParamPhoneUseId(phone, userId);
        //通过该手机号查询该用户是否存在
        UserInfo userInfo = userInfoService.selectUseInfoPhone(phone);
        if (userInfo != null) {
            log.info("该手机号已经存在{}", phone);
            throw new ServiceException("该手机号已注册");
        }
        userInfo = new UserInfo();
        userInfo.setNickname(Utility.randomName());
        userInfo.setPassword(Utility.randomPwd());
        userInfo.setType(AccountType.User);
        userInfo.setPhone(phone);
        userInfo.setValid(true);
        userInfo.setParentId(userId);
        userInfo.setSourceType(sourceType);
        userInfo.setIsDel(1);
        userInfo.setIsVip(0);
//        userInfo.setInviteCode(Utility.invateCode());
        userInfo.setStatus(1);
        //保存注册用户信息，并给分享人送10元券，并且给注册人发送密码短信
        userInfoService.phoneLogin(userInfo);
        return ResponseEntity.ok(AjaxResponseBody.success());
    }

    /**
     * 推广活动中发送验证码使用
     *
     * @param phone
     * @return
     */
//    @ApiIgnore
    @ApiOperation(value = "推广活动中发送验证码使用", notes = "推广活动中发送验证码使用")
    @ApiImplicitParam(paramType = "query", name = "phone", value = "手机号", required = true, dataType = "String")
    @PostMapping(value = "/recommend/login")
    public ResponseEntity<?> recommendLogin(@RequestParam(name = "phone") String phone) {
        checkParamPhone(phone);
        sendMessage(phone);
        return ResponseEntity.ok(AjaxResponseBody.success());
    }

    /**
     * 推广活动验证验证码是否正确
     *
     * @param phone
     * @return
     */
    @ApiOperation(value = "推广活动验证验证码是否正确", notes = "推广活动验证验证码是否正确")
    @ApiImplicitParams({
            @ApiImplicitParam(paramType = "query", name = "phone", value = "手机号", required = true, dataType = "String"),
            @ApiImplicitParam(paramType = "query", name = "code", value = "验证码", required = true, dataType = "String")
    })
    @PostMapping(value = "/recommend/check/code")
    public ResponseEntity<?> checkCode(@RequestParam(name = "phone") String phone,
                                       @RequestParam(name = "code") String code) {
        checkParamPhone(phone);
        boolean flag = smsService.verifySmsForSignup(phone, code);
        String token = null;
        if (flag) {
            //通过该手机号查询该用户是否存在
            UserInfo userInfo = userInfoService.selectUseInfoPhone(phone);
            if (userInfo != null) {
                token = tokenService.createToken(String.valueOf(userInfo.getId()));
            } else {
                userInfo = new UserInfo();
                userInfo.setNickname(Utility.randomName());
                userInfo.setPassword(Utility.randomPwd());
                userInfo.setType(AccountType.User);
                userInfo.setPhone(phone);
                userInfo.setValid(true);
                userInfo.setIsDel(1);
                userInfo.setIsVip(0);
                userInfo.setStatus(1);
                userInfoService.insertReturnId(userInfo);
                if (userInfo.getId() != null) {
                    userInfo.setInviteCode(InvateCode.toSerialCode(userInfo.getId()));
                    int res = userInfoService.updateUserInfo(userInfo);
                    if (res != 1) {
                        log.info("更新邀请码异常");
                    }
                    //给新用户发两张10元优惠券
                    userCouponService.createNewCoupon(userInfo.getId());
                }
                sendMessage(phone);
                token = tokenService.createToken(String.valueOf(userInfo.getId()));
            }
        } else {
            throw new ServiceException("验证码输入有误");
        }
        return ResponseEntity.ok(AjaxResponseBody.success(token));
    }

    /**
     * 判断当前用户是否存在
     *
     * @param phone
     * @return
     */
    @ApiOperation(value = "判断当前用户是否存在", notes = "通过手机号查询该用户是否存在")
    @ApiImplicitParam(paramType = "query", name = "phone", value = "手机号", required = true, dataType = "String")
    @PostMapping(value = "/userInfo")
    public ResponseEntity<?> userInfo(@RequestParam(name = "phone") String phone) {
        log.info("获取当前用户手机号{}", phone);
        checkParamPhone(phone);
        UserInfo operatorInfo = userInfoService.selectUseInfoPhone(phone);
        if (operatorInfo == null) {
            return ResponseEntity.ok(AjaxResponseBody.success(false));
        }
        return ResponseEntity.ok(AjaxResponseBody.success(true));
    }

    /**
     * 用户设置密码或忘记密码
     *
     * @param token
     * @param phone
     * @param password
     * @return
     */
    @ApiOperation(value = "用户设置密码或忘记密码", notes = "设置密码传入token和密码，忘记密码需先验证手机号，再传手机号和密码")
    @ApiImplicitParams({
            @ApiImplicitParam(paramType = "query", name = "token", value = "token", dataType = "String"),
            @ApiImplicitParam(paramType = "query", name = "phone", value = "手机号", required = true, dataType = "String"),
            @ApiImplicitParam(paramType = "query", name = "password", value = "密码", required = true, dataType = "String")
    })
    @PostMapping(value = "/setPwd")
    public ResponseEntity<?> userSetPwd(@RequestParam(name = "token", required = false) String token,
                                        @RequestParam(name = "phone") String phone,
                                        @RequestParam(name = "password") String password
    ) {
        checkParamPhonePwd(phone, password);
        if (Arguments.isEmpty(token)) {//忘记密码，参数手机号和密码
            UserInfo userInfo = userInfoService.selectUseInfoPhone(phone);
            int rec = userInfoService.modifyPasswordById(userInfo.getId(), password);
            if (rec == 0) {
                throw new ServiceException("修改数据失败");
            }
            return ResponseEntity.ok(AjaxResponseBody.success());
        }
        //第一次设置密码，token和密码
        UserInfo operatorInfo = userInfoService.tokenUserInfo(token);
        if (!operatorInfo.getPhone().equals(phone)) {
            throw new ServiceException("拒绝非本人操作");
        }
        int rec = userInfoService.modifyPasswordById(operatorInfo.getId(), password);
        if (rec == 0) {
            throw new ServiceException("修改数据失败");
        }
        return ResponseEntity.ok(AjaxResponseBody.success());
    }

    /**
     * 用户修改密码
     *
     * @param token
     * @param origPwd
     * @param id
     * @param password
     * @return
     */
    @ApiOperation(value = "用户修改密码", notes = "注意验证两次密码输入是否一致")
    @ApiImplicitParams({
            @ApiImplicitParam(paramType = "query", name = "token", value = "token", required = true, dataType = "String"),
            @ApiImplicitParam(paramType = "query", name = "origPwd", value = "原始密码", required = true, dataType = "String"),
            @ApiImplicitParam(paramType = "query", name = "id", value = "用户id", required = true, dataType = "int"),
            @ApiImplicitParam(paramType = "query", name = "password", value = "新密码", required = true, dataType = "String")
    })
    @PostMapping(value = "/modifyPassword")
    public ResponseEntity<?> modifyPassword(@RequestParam(name = "token") String token,
                                            @RequestParam(name = "origPwd") String origPwd,
                                            @RequestParam(name = "id") Integer id,
                                            @RequestParam(name = "password") String password) {

        checkParamPwd(password);
        UserInfo operatorInfo = userInfoService.tokenUserInfo(token);
        if (!operatorInfo.getId().equals(id)) {
            throw new ServiceException("当前操作人非本人");
        }
        UserInfo origAccountInfo = userInfoService.selectByPrimaryKey(id);
        if (!origPwd.equals(origAccountInfo.getPassword())) {
            throw new ServiceException("原密码输入有误");
        }

        int rec = userInfoService.modifyPasswordById(id, password);
        if (rec == 0) {
            throw new ServiceException("更新密码失败");
        }

        return ResponseEntity.ok(AjaxResponseBody.success());
    }

    /**
     * 用户上传头像
     *
     * @return
     */
    @ApiOperation(value = "用户上传头像", notes = "用户上传头像")
    @PostMapping(value = "/upload/avatar")
    public ResponseEntity<?> uploadAvatar(@ApiParam(value = "token", name = "token", required = true) @RequestParam(name = "token") String token,
                                          @ApiParam(value = "上传的文件", name = "file", required = true) @RequestParam(name = "file") MultipartFile file) {
        UserInfo userInfo = userInfoService.tokenUserInfo(token);
        Map<String, Object> map = ossUpload(userInfo.getId(), file);
        if (map.size() == 0) {
            throw new ServiceException("文件上传失败");
        }
        String url = (String) map.get("url");
        Integer res = userInfoService.updateAvatar(userInfo.getId(), url);
        if (res != 1) {
            throw new ServiceException("更新用户头像失败");
        }
        return ResponseEntity.ok(AjaxResponseBody.success(map));
    }

    /**
     * 用户绑定银行卡
     *
     * @return
     */
    @ApiOperation(value = "用户绑定获取修改银行卡", notes = "用户绑定获取修改银行卡")
    @ApiImplicitParams({
            @ApiImplicitParam(paramType = "query", name = "token", value = "token", required = true, dataType = "String"),
            @ApiImplicitParam(paramType = "query", name = "bankCard", value = "银行卡号", required = true, dataType = "String"),
            @ApiImplicitParam(paramType = "query", name = "bankName", value = "银行名称", required = true, dataType = "String"),
            @ApiImplicitParam(paramType = "query", name = "userName", value = "用户真实名称", required = true, dataType = "String")
    })
    @PostMapping(value = "/bank/card")
    public ResponseEntity<?> bankCard(@RequestParam(name = "token") String token,
                                      @RequestParam(name = "bankCard") String bankCard,
                                      @RequestParam(name = "bankName") String bankName,
                                      @RequestParam(name = "userName") String userName) {
        checkParamBank(token, bankCard, bankName, userName);
        UserInfo userInfo = userInfoService.tokenUserInfo(token);
        Boolean flag = userInfoService.bankCard(userInfo, bankCard, bankName, userName);
        return ResponseEntity.ok(AjaxResponseBody.success(flag));
    }

    /**
     * 获取用户绑卡信息
     *
     * @return
     */
    @ApiOperation(value = "获取用户绑卡信息", notes = "获取用户绑卡信息", response = BankBindDTO.class)
    @ApiImplicitParam(paramType = "query", name = "token", value = "token", required = true, dataType = "String")
    @PostMapping(value = "/bank/card/info")
    public ResponseEntity<?> bankCardInfo(@RequestParam(name = "token") String token) {
        UserInfo userInfo = userInfoService.tokenUserInfo(token);
        return ResponseEntity.ok(AjaxResponseBody.success(userInfoService.bankCardInfo(userInfo)));
    }


    private void checkParamBank(String token, String bankCard, String bankName, String userName) {
        if (!Arguments.notEmpty(token)) {
            throw new ServiceException("token不能为空");
        }
        if (!Arguments.notEmpty(bankCard)) {
            throw new ServiceException("银行卡号输入为空");
        }
        if (!TextValidator.ID.boolCheck(bankCard)) {
            throw new ServiceException("银行卡号只能是数字");
        }
        if (bankCard.length() < 16 || bankCard.length() > 19) {
            throw new ServiceException("请输入正确的银行卡号");
        }
        if (!Arguments.notEmpty(bankName)) {
            throw new ServiceException("营养名称输入为空");
        }
        if (!Arguments.notEmpty(userName)) {
            throw new ServiceException("用户真实姓名输入为空");
        }
    }


    private Map<String, Object> ossUpload(Integer userId, MultipartFile file) {

        Map<String, Object> result = new HashMap<>();
        try {
            if (!file.isEmpty()) {
                String filename = file.getOriginalFilename();
                String newName = getFileName(FilenameUtils.getExtension(filename));
                String ossKey = "User/" + userId.toString() + "/" + newName;
                if (ossService.writeOSS(file.getBytes(), ossKey)) {
                    result.put("url", ossService.getPrefixUrl() + ossKey);
                    return result;
                }
            }
        } catch (Exception e) {
            log.info("文件上传出错{}", e.getLocalizedMessage());
            return result;
        }
        return result;
    }


    /**
     * 通过原始名称获取随机名称的图片名
     */
    private static String getFileName(String ext) {
        Date date = new Date(System.currentTimeMillis());
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyyMMddHHmmssSSS");
        String name = simpleDateFormat.format(date);
        return name + getSixNumStr() + "." + ext;
    }

    /**
     * 获取六位数字随机数
     */
    private static String getSixNumStr() {
        Random random = new Random();
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < 6; i++) {
            sb.append(random.nextInt(10));
        }
        return sb.toString();
    }

    private ResponseEntity<?> sendMessage(String phone) {
        try {
            smsService.sendSmsForSignup(phone);
            return ResponseEntity.ok(AjaxResponseBody.success(true));
        } catch (SmsSendException e) {
            if (e.getWait() == 0) {
                throw new ServiceException("短信服务器内部错误");
            } else {
                throw new ServiceException("一分钟限发一条");
            }
        } catch (ServiceException e) {
            // 其它写入失败
            return ResponseEntity.ok(AjaxResponseBody.failed(-1, e.getMessage()));
        } catch (Throwable e) {
            // 其它写入失败
            return ResponseEntity.ok(AjaxResponseBody.failed(-1, "fail"));
        }
    }

    private UserInfo getUserInfo(String sourceType, String phone) {
        UserInfo userInfo = new UserInfo();
        userInfo.setNickname(Utility.randomName());
        userInfo.setPassword(null);
        userInfo.setType(AccountType.User);
        userInfo.setPhone(phone);
        userInfo.setValid(true);
        userInfo.setStatus(1);
        userInfo.setSourceType(sourceType);
        userInfo.setIsVip(0);
        userInfo.setIsDel(1);
        return userInfo;
    }

    private ResponseEntity<?> packLoginResult(UserInfo userInfo) {
        UserInfoDTO userInfoDTO = ObjectConvertUtil.convertObject(userInfo, UserInfoDTO.class);
        // 用户ID生成Token
        String token = userInfoService.getTokenByUseId(userInfo.getId());
        // 生成结果
        Map<String, Object> res = new HashMap<>();
        res.put("user_info", userInfoDTO);
        res.put("token", token);
        if (userInfo.getIsVip() == null || userInfo.getIsVip() == 0) {
            userInfoDTO.setVipStatus(null);
        } else {
            userInfoDTO.setVipStatus(userInfo.getIsVip());
        }
        return ResponseEntity.ok(AjaxResponseBody.success(res));
    }

    private void checkParamPhone(String phone) {
        if (Arguments.isEmpty(phone)) {
            throw new ServiceException("手机号不能为空");
        }
        if (!TextValidator.MOBILE.boolCheck(phone)) {
            throw new ServiceException("手机号格式非法");
        }
    }

    private void checkParamPwd(String password) {
        if (Arguments.isEmpty(password)) {
            throw new ServiceException("密码输入为空");
        }
        if (password.length() < 6 || password.length() > 16) {
            throw new ServiceException("密码的有效长度为6-16位");
        }
    }

    private void checkParamPhoneUseId(String phone, Integer userId) {
        if (Arguments.isEmpty(phone)) {
            throw new ServiceException("手机号不能为空");
        }
        if (!TextValidator.MOBILE.boolCheck(phone)) {
            throw new ServiceException("手机号格式非法");
        }
        if (Arguments.isNullOrZero(userId)) {
            throw new ServiceException("分享人id不能为空");
        }
    }

    private void checkParamPhonePwd(String phone, String password) {
        if (Arguments.isEmpty(phone)) {
            throw new ServiceException("手机号不能为空");
        }
        if (!TextValidator.MOBILE.boolCheck(phone)) {
            throw new ServiceException("手机号格式非法");
        }
        if (Arguments.isEmpty(password)) {
            throw new ServiceException("密码输入为空");
        }
        if (password.length() < 6 || password.length() > 16) {
            throw new ServiceException("密码的有效长度为6-16位");
        }
    }
}
